﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AdaptiveDeNet.CSV;
using AdaptiveDeNet.RAND;
using AdaptiveDeNet.DifferentialEvolution.Common;

namespace AdaptiveDeNet.DifferentialEvolution
{
    public partial class Optimizer
    {
        /// <summary>
        /// b6e6rl competitive DE, tries to find global minimum point of given function
        /// </summary>
        /// <param name="lowerBound">Lower bound vector</param>
        /// <param name="upperBound">Upper bound vector</param>
        /// <param name="maxEvals">maximum evaluations of objective function</param>
        /// <param name="dim">problem dimesion</param>
        /// <param name="N">size of population</param>
        /// <param name="myEpps">small nonnegative value, when (Fmax - Fmin) is smaller then myEpps, the termination criteria are satisfied</param>
        /// <param name="n0">controls changes in probability of strategy selection</param>
        /// <param name="delta">critial minimal value of strategy propability</param>
        /// <returns></returns>
        public Result de_b6e6rl(double[] lowerBound,
            double[] upperBound, int maxEvals, int dim, int N, double myEpps, double n0, double delta)
        {
            this.nRand = new Rand();

            double Fmin = double.MaxValue;
            double Fmax = double.MinValue;

            //deklarace hlavni populace
            Member[] mainPop = new Member[N];
            Member[] trials = new Member[N];

            Member trial;

            //inicializace
            // x = rand(0,1) * (uB - lB) + lb

            double[] uBlBDiff = new double[dim];

            for (int i = 0; i < dim; i++)
            {
                uBlBDiff[i] = upperBound[i] - lowerBound[i];
            }

            for (int i = 0; i < N; i++)
            {
                Member newMember = new Member(dim);

                // pro kazdou dimenzi
                for (byte j = 0; j < dim; j++)
                {
                    newMember.Vector[j] = nRand.NextDouble() * uBlBDiff[j] + lowerBound[j];
                }

                //evaluace
                newMember.FEval = fEval.Feval(newMember.Vector);
                // zjisteni min. hodnoty  funkce v nulte populaci
                if (newMember.FEval < Fmin) Fmin = newMember.FEval;

                mainPop[i] = newMember;
            }

            Fmax = Common.Common.FindMax(mainPop);
            //Fmax = this.FindMax(mainPopulation);
            //Fmin = this.NajdiMin(mainPopulation);
            int popEvals = N;

            StrategyPool pool = new StrategyPool(dim, N, lowerBound, upperBound, nRand, n0, delta);

            //main loop
            while (popEvals < maxEvals && (Fmax - Fmin) > myEpps)
            {
                //vygenerovani  
                for (int i = 0; i < N; i++)
                {

                    Tuple<int, Member> t1 = pool.GetTrialMember(mainPop, i);

                    trial = t1.Item2;

                    //evaluace  fce
                    trial.FEval = this.fEval.Feval(trial.Vector);
                    trials[i] = trial;
                    popEvals++;

                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        pool.EvalStrategyPool(t1.Item1);
                    }
                }

                for (int i = 0; i < N; i++)
                {
                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        mainPop[i] = trials[i];
                        if (trials[i].FEval < Fmin) Fmin = trials[i].FEval;
                    }
                }

                Fmax = Common.Common.FindMax(mainPop);
                //Fmax = this.FindMax(mainPopulation);
                //Fmin = this.NajdiMin(mainPopulation);

            }

            Result result = new Result();

            result.FuncEvals = popEvals;
            result.Fmin = Fmin;
            result.Point = Common.Common.GetBestMember(mainPop);

            return result;

        }

        /// <summary>
        /// b6e6rl competitive DE, tries to find global minimum point of given function
        /// </summary>
        /// <param name="function">objective function</param>
        /// <param name="lowerBound">Lower bound vector</param>
        /// <param name="upperBound">Upper bound vector</param>
        /// <param name="maxEvals">maximum evaluations of objective function</param>
        /// <param name="dim">problem dimesion</param>
        /// <param name="N">size of population</param>
        /// <param name="myEpps">small nonnegative value, when (Fmax - Fmin) is smaller then myEpps, the termination criteria are satisfied</param>
        /// <param name="n0">controls changes in probability of strategy selection</param>
        /// <param name="delta">critial minimal value of strategy propability</param>
        /// <returns></returns>
        public Result de_b6e6rl(Func<double[], double> function, double[] lowerBound,
            double[] upperBound, int maxEvals, int dim, int N, double myEpps, double n0, double delta)
        {
            this.nRand = new Rand();

            double Fmin = double.MaxValue;
            double Fmax = double.MinValue;

            //deklarace hlavni populace
            Member[] mainPop = new Member[N];
            Member[] trials = new Member[N];

            Member trial;

            //inicializace
            // x = rand(0,1) * (uB - lB) + lb

            double[] uBlBDiff = new double[dim];

            for (int i = 0; i < dim; i++)
            {
                uBlBDiff[i] = upperBound[i] - lowerBound[i];
            }

            for (int i = 0; i < N; i++)
            {
                Member newMember = new Member(dim);

                // pro kazdou dimenzi
                for (byte j = 0; j < dim; j++)
                {
                    newMember.Vector[j] = nRand.NextDouble() * uBlBDiff[j] + lowerBound[j];
                }

                //evaluace
                newMember.FEval = function.Invoke(newMember.Vector);
                // zjisteni min. hodnoty  funkce v nulte populaci
                if (newMember.FEval < Fmin) Fmin = newMember.FEval;

                mainPop[i] = newMember;
            }

            Fmax = Common.Common.FindMax(mainPop);
            //Fmax = this.FindMax(mainPopulation);
            //Fmin = this.NajdiMin(mainPopulation);
            int popEvals = N;

            StrategyPool pool = new StrategyPool(dim, N, lowerBound, upperBound, nRand, n0, delta);

            //main loop
            while (popEvals < maxEvals && (Fmax - Fmin) > myEpps)
            {
                //vygenerovani  
                for (int i = 0; i < N; i++)
                {

                    Tuple<int, Member> t1 = pool.GetTrialMember(mainPop, i);

                    trial = t1.Item2;

                    //evaluace  fce
                    trial.FEval = function.Invoke(trial.Vector);
                    trials[i] = trial;
                    popEvals++;

                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        pool.EvalStrategyPool(t1.Item1);
                    }
                }

                for (int i = 0; i < N; i++)
                {
                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        mainPop[i] = trials[i];
                        if (trials[i].FEval < Fmin) Fmin = trials[i].FEval;
                    }
                }

                Fmax = Common.Common.FindMax(mainPop);
                //Fmax = this.FindMax(mainPopulation);
                //Fmin = this.NajdiMin(mainPopulation);

            }

            Result result = new Result();

            result.FuncEvals = popEvals;
            result.Fmin = Fmin;
            result.Point = Common.Common.GetBestMember(mainPop);

            return result;

        }

        public CEC14Result de_b6e6rl(double[] lowerBound,
            double[] upperBound, int maxEvals, int dim, int N, double myEpps, double n0, double delta, Queue<double> FESMultipliers, double fX)
        {
            CEC14Result result = new CEC14Result();

            this.nRand = new Rand();

            double Fmin = double.MaxValue;
            double Fmax = double.MinValue;

            //deklarace hlavni populace
            Member[] mainPop = new Member[N];
            Member[] trials = new Member[N];

            Member trial;

            //inicializace
            // x = rand(0,1) * (uB - lB) + lb

            double[] uBlBDiff = new double[dim];

            for (int i = 0; i < dim; i++)
            {
                uBlBDiff[i] = upperBound[i] - lowerBound[i];
            }

            for (int i = 0; i < N; i++)
            {
                Member newMember = new Member(dim);

                // pro kazdou dimenzi
                for (byte j = 0; j < dim; j++)
                {
                    newMember.Vector[j] = nRand.NextDouble() * uBlBDiff[j] + lowerBound[j];
                }

                //evaluace
                newMember.FEval = fEval.Feval(newMember.Vector);
                // zjisteni min. hodnoty  funkce v nulte populaci
                if (newMember.FEval < Fmin) Fmin = newMember.FEval;

                mainPop[i] = newMember;
            }

            Fmax = Common.Common.FindMax(mainPop);
            //Fmax = this.FindMax(mainPopulation);
            //Fmin = this.NajdiMin(mainPopulation);
            int popEvals = N;

            StrategyPool pool = new StrategyPool(dim, N, lowerBound, upperBound, nRand, n0, delta);

            //main loop
            while (popEvals < maxEvals)
            {
                //vygenerovani  
                for (int i = 0; i < N; i++)
                {

                    Tuple<int, Member> t1 = pool.GetTrialMember(mainPop, i);

                    trial = t1.Item2;

                    //evaluace  fce
                    trial.FEval = this.fEval.Feval(trial.Vector);
                    trials[i] = trial;
                    popEvals++;

                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        pool.EvalStrategyPool(t1.Item1);
                    }
                }

                for (int i = 0; i < N; i++)
                {
                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        mainPop[i] = trials[i];
                        if (trials[i].FEval < Fmin) Fmin = trials[i].FEval;
                    }
                }

                if (popEvals == (FESMultipliers.Peek() * maxEvals))
                {
                    result.Records.Add(Fmin - fX);
                    FESMultipliers.Dequeue();
                }

                Fmax = Common.Common.FindMax(mainPop);
                //Fmax = this.FindMax(mainPopulation);
                //Fmin = this.NajdiMin(mainPopulation);

            }

            result.FuncEvals = popEvals;

            return result;
        }

    }
}
